import * as THREE from "./build/three.module.js";

import { OrbitControls } from "./libs/jsm/controls/OrbitControls.js";
import { STLLoader } from "./libs/jsm/loaders/STLLoader.js";
import { MTLLoader } from "./libs/jsm/loaders/MTLLoader.js";
import { OBJLoader2 } from "./libs/jsm/loaders/OBJLoader2.js";
import { MtlObjBridge } from "./libs/jsm/loaders/obj2/bridge/MtlObjBridge.js";

var container, controls;
var camera,
  scene,
  renderer,
  light,
  ambientLight,
  cubeMap,
  group,
  groupJZ,
  mixer,
  tween;
var city;
var clipMesh;
var isFocusBuilding = false,
  isFocusFloor = false;
var clock = new THREE.Clock();
var raycaster = new THREE.Raycaster();
var mouseVector = new THREE.Vector3();
window.loaded = {};

init();
animate();

function init() {
  container = document.createElement("div");
  document.body.appendChild(container);

  camera = new THREE.PerspectiveCamera(
    45,
    window.innerWidth / window.innerHeight,
    1,
    5000
  );
  camera.position.set(500, 10000, 18000);

  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xd6e0eb);
  scene.fog = new THREE.Fog(0xa0a0a0, 1, 8000);

  group = new THREE.Group();
  group.renderOrder = 0;
  scene.add(group);
  groupJZ = new THREE.Group();
  groupJZ.renderOrder = 1;
  scene.add(groupJZ);

  light = new THREE.HemisphereLight(0x999999, 0x000000);
  light.position.set(0, 10000, 0);
  scene.add(light);

  light = new THREE.DirectionalLight(0xffffff);
  light.position.set(0, 1200, 200);
  light.castShadow = true;
  light.shadow.camera.top = 180;
  light.shadow.camera.bottom = -100;
  light.shadow.camera.left = -120;
  light.shadow.camera.right = 120;
  scene.add(light);

  var url = "./resource/images/skybox";
  var suffix = ".png";
  var urls = [
    `${url}/px${suffix}`,
    `${url}/nx${suffix}`,
    `${url}/py${suffix}`,
    `${url}/ny${suffix}`,
    `${url}/pz${suffix}`,
    `${url}/nz${suffix}`,
  ];
  cubeMap = new THREE.CubeTextureLoader().load(urls);
  scene.background = cubeMap;

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    logarithmicDepthBuffer: true,
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;
  container.appendChild(renderer.domElement);

  controls = new OrbitControls(camera, renderer.domElement);
  controls.target.set(600, 100, 400);
  // controls.minPolarAngle = Math.PI * 0.15;
  // controls.maxPolarAngle = Math.PI * 0.5;
  controls.minDistance = 30;
  controls.maxDistance = 2000;
  controls.update();
  controls.setAngle = function (phi, theta, distance) {
    var r = distance || controls.object.position.distanceTo(controls.target);
    var x =
      r * Math.cos(phi - Math.PI / 2) * Math.sin(theta) + controls.target.x;
    var y = r * Math.sin(phi + Math.PI / 2) + controls.target.y;
    var z =
      r * Math.cos(phi - Math.PI / 2) * Math.cos(theta) + controls.target.z;
    controls.object.position.set(x, y, z);
    controls.object.lookAt(controls.target);
  };
  // window.addEventListener('resize', onWindowResize, false);
  window.addEventListener("dblclick", onDocumentMouseClick, false);
  // scene.add(new THREE.AxisHelper(10000));
}

window.loadCity = function () {
  return new Promise((resolve) => {
    var modelName = "SMALL CITY";
    let objLoader2 = new OBJLoader2();
    objLoader2.setLogging(false, false);
    let callbackOnLoad = function (object3d) {
      city = object3d;
      object3d.scale.set(0.3, 0.3, 0.3);
      object3d.rotation.set(0, Math.PI / 2, 0);
      object3d.position.set(750, -50, 250);
      scene.add(object3d);
      var endDate = new Date();
      console.info(
        `${modelName}: `,
        (endDate.getTime() - beginDate.getTime()) / 1000,
        "s"
      );
      resolve && resolve(true);
    };

    let onLoadMtl = function (mtlParseResult) {
      objLoader2.setModelName(modelName);
      objLoader2.addMaterials(
        MtlObjBridge.addMaterialsFromMtlLoader(mtlParseResult)
      );
      objLoader2.load(
        "./models/obj/smallcity.obj",
        callbackOnLoad,
        null,
        null,
        null
      );
    };
    var beginDate = new Date();
    showMessage(`loading ${modelName}`);
    showMask();
    let mtlLoader = new MTLLoader();
    mtlLoader.load("./models/obj/smallcity.mtl", onLoadMtl);
  });
};

function loadCameras(isChecked) {
  var promises = [];
  if (window.bimData && window.bimData.cameras) {
    var cameras = window.bimData.cameras;
    for (let i = 0; i < cameras.length; i++) {
      promises.push(loadACamera(isChecked, cameras[i].id, cameras[i].position));
    }
  }
  return Promise.all(promises);
}

function loadSmokes(isChecked) {
  var promises = [];
  if (window.bimData && window.bimData.smokes) {
    var smokes = window.bimData.smokes;
    for (let i = 0; i < smokes.length; i++) {
      promises.push(loadASmoke(isChecked, smokes[i].id, smokes[i].position));
    }
  }
  return Promise.all(promises);
}

function loadFires(isChecked) {
  var promises = [];
  if (window.bimData && window.bimData.fires) {
    var fires = window.bimData.fires;
    for (let i = 0; i < fires.length; i++) {
      promises.push(loadAFire(isChecked, fires[i].id, fires[i].position));
    }
  }
  return Promise.all(promises);
}

function loadFloorObjects(isChecked) {
  return loadCameras(isChecked)
    .then((_) => loadSmokes(isChecked))
    .then((_) => loadFires(isChecked));
}

function loadCamera(modelName, source, x, y, z) {
  showMessage(`loading ${modelName}`);
  showMask();
  return new Promise((resolve) => {
    let objLoader2 = new OBJLoader2();
    let callbackOnLoad = function (object3d) {
      object3d.scale.set(0.2, 0.2, 0.2);
      object3d.position.x = x;
      object3d.position.y = y;
      object3d.position.z = z;
      object3d.myname = modelName;
      scene.add(object3d);
      loaded[modelName] = object3d;
      object3d.children.forEach((c) => {
        c.myname = modelName;
        c.material.transparent = true;
        c.material.opacity = 0.5;
        // c.material.color.set(0xFFF000);
      });
      group.add(object3d);

      resolve && resolve(true);
    };
    let onLoadMtl = function (mtlParseResult) {
      objLoader2.setModelName(modelName);
      objLoader2.addMaterials(
        MtlObjBridge.addMaterialsFromMtlLoader(mtlParseResult)
      );
      objLoader2.load(
        `./models/obj/${source}.obj`,
        callbackOnLoad,
        null,
        null,
        null
      );
    };
    let mtlLoader = new MTLLoader();
    mtlLoader.load(`./models/obj/${source}.mtl`, onLoadMtl);
  });
}

function loadSmoke(modelName, source, x, y, z) {
  showMessage(`loading ${modelName}`);
  showMask();
  return new Promise((resolve) => {
    let objLoader2 = new OBJLoader2();
    let callbackOnLoad = function (object3d) {
      object3d.scale.set(0.1, 0.1, 0.1);
      object3d.position.x = x;
      object3d.position.y = y;
      object3d.position.z = z;
      object3d.myname = modelName;
      scene.add(object3d);
      loaded[modelName] = object3d;
      object3d.children.forEach((c) => {
        c.myname = modelName;
        c.material.transparent = true;
        c.material.opacity = 0.5;
        // c.material.color.set(0xFFF000);
      });
      group.add(object3d);

      resolve && resolve(true);
    };
    let onLoadMtl = function (mtlParseResult) {
      objLoader2.setModelName(modelName);
      objLoader2.addMaterials(
        MtlObjBridge.addMaterialsFromMtlLoader(mtlParseResult)
      );
      objLoader2.load(
        `./models/obj/${source}.obj`,
        callbackOnLoad,
        null,
        null,
        null
      );
    };
    let mtlLoader = new MTLLoader();
    mtlLoader.load(`./models/obj/${source}.mtl`, onLoadMtl);
  });
}

function loadFire(modelName, source, x, y, z) {
  showMessage(`loading ${modelName}`);
  showMask();
  return new Promise((resolve) => {
    let objLoader2 = new OBJLoader2();
    let callbackOnLoad = function (object3d) {
      object3d.scale.set(0.5, 0.5, 0.5);
      object3d.position.x = x;
      object3d.position.y = y;
      object3d.position.z = z;
      object3d.myname = modelName;
      scene.add(object3d);
      loaded[modelName] = object3d;
      object3d.children.forEach((c) => {
        c.myname = modelName;
        // if (c.material instanceof Array) {
        //     c.material.forEach(m => {
        //         m.color.set(0xFFF000);
        //     });
        // } else {
        //     c.material.color.set(0xFFF000);
        // }
      });
      group.add(object3d);

      resolve && resolve(true);
    };
    let onLoadMtl = function (mtlParseResult) {
      objLoader2.setModelName(modelName);
      objLoader2.addMaterials(
        MtlObjBridge.addMaterialsFromMtlLoader(mtlParseResult)
      );
      objLoader2.load(
        `./models/obj/${source}.obj`,
        callbackOnLoad,
        null,
        null,
        null
      );
    };
    let mtlLoader = new MTLLoader();
    mtlLoader.load(`./models/obj/${source}.mtl`, onLoadMtl);
  });
}

function loadACamera(isChecked, id, position) {
  let p = new Promise((resolve) => resolve(isChecked));
  if (isChecked) {
    if (loaded[id]) {
      var model = loaded[id];
      model.children.forEach((c) => {
        tm(c, 1);
      });
    } else {
      p = loadCamera(
        id,
        "camera3/banqiu",
        position[0],
        position[1],
        position[2]
      );
    }
  } else {
    var model = loaded[id];
    model.children.forEach((c) => {
      tm(c, 0);
    });
  }
  return p;
}

function loadASmoke(isChecked, id, position) {
  let p = new Promise((resolve) => resolve(isChecked));
  if (isChecked) {
    if (loaded[id]) {
      var model = loaded[id];
      model.children.forEach((c) => {
        tm(c, 1);
      });
    } else {
      p = loadSmoke(id, "yangan/yangan", position[0], position[1], position[2]);
    }
  } else {
    var model = loaded[id];
    model.children.forEach((c) => {
      tm(c, 0);
    });
  }
  return p;
}

function loadAFire(isChecked, id, position) {
  let p = new Promise((resolve) => resolve(isChecked));
  if (isChecked) {
    if (loaded[id]) {
      var model = loaded[id];
      model.children.forEach((c) => {
        tm(c, 1);
      });
    } else {
      p = loadFire(id, "fire/fire", position[0], position[1], position[2]);
    }
  } else {
    var model = loaded[id];
    model.children.forEach((c) => {
      tm(c, 0);
    });
  }
  return p;
}

window.loadModel = function (modelName, color) {
  var loader = new STLLoader();
  var material = new THREE.MeshPhongMaterial({
    color: color,
    specular: 0x000000,
    shininess: 10,
  });
  material.transparent = true;
  showMessage(`loading ${modelName}`);
  showMask();
  return new Promise((resolve, reject) => {
    loader.load(`./models/stl/${modelName}.stl`, function (geometry) {
      var mesh = new THREE.Mesh(geometry, material);
      mesh.position.set(-150, -250, 140);
      if (modelName == "JG" || modelName == "JZ") {
        mesh.position.x = -155;
        mesh.position.z = 160;
      }
      if (modelName != "JZ") {
        material.opacity = 0.5;
      }
      if (modelName == "D") {
        mesh.position.x = -180;
        mesh.position.z = 160;
      }
      if (modelName == "S") {
        mesh.position.x = -180;
        mesh.position.z = 160;
      }
      mesh.myname = modelName;
      mesh.rotation.set(-Math.PI / 2, 0, 0);
      mesh.scale.set(0.12, 0.12, 0.24);

      mesh.castShadow = true;
      mesh.receiveShadow = true;

      group.add(mesh);
      loaded[modelName] = mesh;
      resolve && resolve(true);
    });
  });
};

window.loadBuilding = function () {
  var modelName = "JZ";
  var loader = new STLLoader();
  var material = new THREE.MeshPhongMaterial({
    color: 0xcccccc,
    specular: 0x000000,
    shininess: 10,
  });
  material.transparent = true;
  showMessage(`loading ${modelName}`);
  showMask();
  return new Promise((resolve, reject) => {
    loader.load(`./models/stl/${modelName}.stl`, function (geometry) {
      var mesh = new THREE.Mesh(geometry, material);
      mesh.oldMaterial = material;
      mesh.position.set(-105, 0, 110);
      mesh.myname = modelName;
      mesh.rotation.set(-Math.PI / 2, 0, 0);
      mesh.scale.set(0.12, 0.12, 0.24);

      mesh.castShadow = true;
      mesh.receiveShadow = true;

      groupJZ.add(mesh);
      loaded[modelName] = mesh;
      resolve && resolve(true);
    });
  });
};

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function onDocumentMouseClick(event) {
  event.preventDefault();
  var intersects = getIntersects(event.layerX, event.layerY);
  // console.log(intersects);
  if (intersects.length > 0) {
    if (!clickModel && !isFocusBuilding) {
      if (intersects[0].object.myname == "JZ") {
        focusBuilding();
      }
    } else {
      // var res = intersects.filter(function (res) {
      //     return res && res.object.myname == clickModel;
      // })[0];
      // if (res && res.object) {
      //     selectedObject = res.object;
      //     createBillboard(clickModel, event.layerX, event.layerY);
      // }
    }
  }
}

window.focusModel = function (modelName, color, position) {
  group.children.forEach((g) => {
    if (g.myname == modelName) {
      if (g.isMesh) {
        g.material.visible = true;
        color && g.material.color.set(color);
        g.material.opacity = 1;
      } else if (g.children) {
        g.children.forEach((c) => {
          c.material.visible = true;
          color && c.material.color.set(color);
          c.material.opacity = 1;
        });
      }
    } else {
      if (g.isMesh) {
        g.material.visible = false;
      } else if (g.children) {
        g.children.forEach((c) => {
          c.material.visible = false;
        });
      }
    }
  });
  animateCamera(position, { x: 0, y: 0, z: 0 });
};

window.focus = function (id, isIncident) {
  var model = loaded[id];
  if (model) {
    var position = model.position;
    animateCamera(
      { x: position.x + 10, y: position.y + 10, z: position.z + 10 },
      position,
      function () {
        showDetail(id, isIncident);
      }
    );
  }
};

window.focusBuilding = function (callback) {
  if (!isFocusBuilding) {
    scene.background = new THREE.Color(0x111111);
    ambientLight = new THREE.AmbientLight(0x333333);
    scene.add(ambientLight);
    city.position.x = 100000;
    loaded.JZ.position.set(-150, -250, 150);
    loaded.JZ.material.color.set(0xffffff);
    loaded.JZ.material.opacity = 0.2;
    isFocusBuilding = true;
    $(".building-menus-wrapper").show();
    $(".floor-select-wrapper").show();
  }
  isSelectCamera = false;
  isSelectSmoke = false;
  isSelectFire = false;
  $(".in-floor").children().removeClass("selected");
  $(".floor-objects").hide();
  top.storageserver && top.storageserver.hide();
  animateCamera({ x: 500, y: 300, z: 500 }, { x: 0, y: 0, z: 0 }, function () {
    callback && callback();
  });
};

window.restore = function () {
  if (isFocusBuilding) {
    resumeFloor();
    scene.background = cubeMap;
    scene.remove(ambientLight);
    city.position.x = 750;
    loaded.JZ.position.set(-105, 0, 110);
    loaded.JZ.material = loaded.JZ.oldMaterial;
    loaded.JZ.material.opacity = 1;

    if (clickModel) {
      var m = loaded[clickModel];
      if (m) {
        if (m.isMesh) {
          tm(m, 0);
        } else if (m.children) {
          m.children.forEach((c) => {
            tm(c, 0);
          });
        }
      }
      clickModel = null;
    }
    isFocusFloor && loadFloorObjects(false);
    isFocusBuilding = false;
    $(".building-menus-wrapper").hide();
    $(".floor-select-wrapper").hide();
  }
  animateCamera({ x: 500, y: 10000, z: 18000 }, { x: 600, y: 100, z: 400 });
};

window.clipFloor = function () {
  var p = new Promise((resolve) => {
    animateCamera(
      { x: 300, y: 300, z: 100 },
      { x: 0, y: 0, z: 0 },
      function () {
        resolve && resolve();
      }
    );
  });
  if (!isFocusFloor) {
    var geometry = new THREE.BoxGeometry(1, 1, 1);
    var PlaneArr = [
      new THREE.Plane(new THREE.Vector3(0, 1, 0), -40),
      new THREE.Plane(new THREE.Vector3(0, -1, 0), 90),
    ];
    var material = new THREE.MeshLambertMaterial({
      color: 0xffffff,
      side: THREE.DoubleSide,
      clippingPlanes: PlaneArr,
      clipIntersection: true,
      transparent: true,
      opacity: 0,
    });
    clipMesh = new THREE.Mesh(geometry, material);
    scene.add(clipMesh);
    isFocusFloor = true;

    renderer.clippingPlanes = PlaneArr;
    renderer.localClippingEnabled = true;

    p = new Promise((resolve) => {
      animateCamera(
        { x: 300, y: 300, z: 100 },
        { x: 0, y: 0, z: 0 },
        function () {
          loadFloorObjects(true).then(hideMask).then(resolve);
        }
      );
    });
  }
  return p;
};

window.unclipFloor = function (callback) {
  renderer.clippingPlanes = [];
  scene.remove(clipMesh);
  isFocusFloor && loadFloorObjects(false);
  isFocusFloor = false;
  animateCamera({ x: 500, y: 300, z: 500 }, { x: 0, y: 0, z: 0 }, function () {
    callback && callback();
  });
};

function resumeFloor() {
  renderer.clippingPlanes = [];
  scene.remove(clipMesh);
  isFocusFloor && loadFloorObjects(false);
  var array = ["S", "D", "KTF", "KTS", "QT", "ZP"];
  array.forEach((a) => {
    let m = loaded[a];
    if (m) {
      if (m.isMesh) {
        tm(m, 0);
      } else if (m.children) {
        m.children.forEach((c) => {
          tm(c, 0);
        });
      }
    }
  });
  clickModel = null;
  currentFloor = false;
  isFocusFloor = false;
  isSelectS = false;
  isSelectD = false;
  isSelectQT = false;
  isSelectZP = false;
  isSelectKTF = false;
  isSelectKTS = false;
  isSelectCamera = false;
  isSelectSmoke = false;
  isSelectFire = false;
  $(".in-building").children().removeClass("selected");
  $(".in-floor").children().removeClass("selected");
  $(".floor-select").children().removeClass("selected");
  $(".floor-select>div:first-child").addClass("selected");
  $(".in-floor").hide();
  $(".floor-objects").hide();
}

function animateCamera(current, target, callback) {
  let positionVar = {
    x1: camera.position.x,
    y1: camera.position.y,
    z1: camera.position.z,
    x2: controls.target.x,
    y2: controls.target.y,
    z2: controls.target.z,
  };
  //关闭控制器
  controls.enabled = false;
  tween = new TWEEN.Tween(positionVar);
  tween.to(
    {
      x1: current.x,
      y1: current.y,
      z1: current.z,
      x2: target.x,
      y2: target.y,
      z2: target.z,
    },
    1000
  );

  tween.onUpdate(function () {
    camera.position.x = positionVar.x1;
    camera.position.y = positionVar.y1;
    camera.position.z = positionVar.z1;
    controls.target.x = positionVar.x2;
    controls.target.y = positionVar.y2;
    controls.target.z = positionVar.z2;
    controls.update();
  });

  tween.onComplete(function () {
    ///开启控制器
    controls.enabled = true;
    callback && callback();
  });

  tween.easing(TWEEN.Easing.Cubic.InOut);
  tween.start();
}

function getIntersects(x, y) {
  x = (x / window.innerWidth) * 2 - 1;
  y = -(y / window.innerHeight) * 2 + 1;
  mouseVector.set(x, y, 0.5);
  raycaster.setFromCamera(mouseVector, camera);
  return raycaster
    .intersectObject(group, true)
    .concat(raycaster.intersectObject(groupJZ, true));
}

function animate() {
  if (!controls.enabled) {
    TWEEN.update();
  }
  renderer.render(scene, camera);
  controls.update();
  var delta = clock.getDelta();
  if (mixer) {
    mixer.update(delta);
  }
  requestAnimationFrame(animate);
}
